// Copyright 2022 Huawei Cloud Computing Technology Co., Ltd.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.


#ifndef CLOUDAPPENGINE_CAESOCKETAGENT_H
#define CLOUDAPPENGINE_CAESOCKETAGENT_H

#include <list>

#include "../cae_socket/CaeSocket.h"
#include "CaeTcpClientErrorTimer.h"
#include "../cae_dataHandler/CaeClientDataHandleThread.h"
#include "../cae_common/CaeConfigManage.h"
#include "CaeClientNotifyTool.h"
#include "../cae_dataHandler/CaeControlCmdHandleThread.h"

class CaeClientNotice : public CaeEventNotice {
public:
    CaeTcpClientErrorTimer *m_errorTimer;

    explicit CaeClientNotice(const string &connectionId)
    {
        Init(connectionId);
    }

    ~CaeClientNotice() override
    {
        if (m_errorTimer != nullptr) {
            delete m_errorTimer;
            m_errorTimer = nullptr;
        }
    }

    void Init(const string &connectionId)
    {
        this->m_errorTimer = new CaeTcpClientErrorTimer(CaeConfigManage::GetOutTimes() *
                                                        CaeConfigManage::GetBaseTime(), connectionId); // ms
        if (this->m_errorTimer == nullptr) {
            LOGE("Init error, new CaeTcpClientErrorTimer fail.");
            return;
        }
        this->m_errorTimer->Start();
        CaeTimerManage::GetInstance().AddTimer(m_errorTimer);
    }

    void Stop() {
        this->m_errorTimer->Stop();
        CaeTimerManage::GetInstance().DeleteTimer(m_errorTimer);
    }

    void SetDisconnectCallback(const TimeoutHook & callback)
    {
        m_errorTimer->SetDisconnectHook(callback);
    }

    void SetDisconnectTimeoutCallback(const TimeoutHook & callback)
    {
        m_errorTimer->SetTimeoutHook(callback);
    }

    void Notice(void *tcpSocket) override
    {
        LOGI("CaeClientSocket: error.");
    }

    void RestartErrorTimer()
    {
        this->m_errorTimer->Restart();
    }
};

namespace CAE {
    enum CaeSocketAgentState {
        INIT                = 0,
        VERIFYING           = 1,
        VERIFYED            = 2,
        RUNNING             = 3,
        PAUSED              = 4, // 进入后台，会停止音视频引擎
        PAUSED_DISCONNECT   = 5, // 进入后台后由于系统后台限制导致断网断链，会停止音视频引擎
        WAIT_RECONNECT      = 6, // 前台网络异常导致的断链，不停止音视频引擎
        WAIT_CLOSE          = 7,
        DEINIT              = 8,
    };
}


class CaeSocketAgent : public std::enable_shared_from_this<CaeSocketAgent> {
public:
    CaeSocketAgent();
    ~CaeSocketAgent();

    void LockClientSocket();

    void UnlockClientSocket();

    bool IsSocketValid();

    void SetClientSocket(CaeSocket *socket);

    void SetIsVerifyComplete(bool mIsVerifyComplete);

    // 仅认证验签的动作是否结束，认证鉴权的结果不一定是成功
    bool IsVerifyComplete() const;

    ServerType GetServerType();

    string GetClientPlatformType();

    string GetSdkVersion();

    int Send(void *pkt, size_t size);

    int Recv(void *pPkt, size_t size);

    void Reset();

    void DisableSend();

    void EnableSend();

    void RestartErrorTimer();

    void CloseOldClient();

    void CloseOldSocket();

    void UpdateErrorTimerTime();

    void UpdateNoTouchTimer();

    bool StartNotouchTimer();

    void StopNotouchTimer();

    void StartPauseTimer();

    void StopPauseTimer();

    bool StartTrailTimer();

    void StopTrailTimer();

    void PauseClient();

    volatile bool m_sendFlag;

    string GetSocketConnectionId();

    void SetSocketConnectionId(string connectionId);

    string GetClientIp();

    void SetClientIp(string clientIp);

    string GetClientPort();

    void SetClientPort(string clientPort);

    void SetSocketAgentState(CAE::CaeSocketAgentState socketAgentState);

    CAE::CaeSocketAgentState GetSocketAgentState() const;
    string GetSocketAgentStateString() const;

    void AddCmdPkt(uint8_t *data, size_t size);
    CaePipeData *GetCmdPkt();

    shared_ptr<CaeParamStorage> GetConnectionParams();
    void SetConnectionParams(shared_ptr<CaeParamStorage> &paramStorage);

    void SetVerifyCallback(const VerifyRetCallback & callback);
    void SetActionCallback(const ActionCallback & callback);

    void SetDisconnectCallback(const TimeoutHook & callback);
    void SetDisconnectTimeoutCallback(const TimeoutHook & callback);
    void SetNoTouchTimeoutCallback(const TimeoutHook & callback);
    void SetTrialTimeoutCallback(const TimeoutHook & callback);
    void SetPauseTimeoutCallback(const TimeoutHook & callback);

private:
    void DestroyThreads() noexcept;
    void CreateClientNotice();

    std::shared_ptr<CaeSocketAgent> GetSelf()
    {
        return shared_from_this();
    }

private:
    CaeSocket *m_clientSocket;
    ClientType m_clientType;
    void *m_notice;
    CaeTcpClientPauseTimer *m_pauseTimer;
    CaeTrialTimeoutTimer *m_trailTimer;
    CaeNotouchTimer *m_notouchTimer;
    pthread_mutex_t m_lock;
    pthread_mutex_t m_clientSocketLock;
    CaeClientDataHandleThread *m_clientDataHandleThread;
    CaeControlCmdHandleThread *m_cmdPipeDataHandleThread;
    string m_connectionId;
    string m_clientIp;
    string m_clientPort;
    bool m_isVerifyComplete = false;

    pthread_mutex_t m_cmdQueueMutex;
    std::queue<CaePipeData *> m_cmdQueue;
    volatile CAE::CaeSocketAgentState m_socketAgentState = CAE::INIT;
};

#endif // CLOUDAPPENGINE_CAESOCKETAGENT_H